//
//	MiscMatrix.m -- a class to implement variable-sized matrices
//		Written by Mike Ferris Copyright (c) 1994 by Mike Ferris.
//		Modified from original MOKit "MOMatrix" class by Don Yacktman.
//				Version 1.0.  All rights reserved.
//
//		This notice may not be removed from this source code.
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	

// MiscMatrix is a subclass of Matrix that allows independantly sizable
// rows and columns.  Each row can have a different height and each column
// can have a different width.
/*
 *	Revision History:
 *		July 1994:	Steve Quirk		steveq@telerate.com
 *			- implemented sizeToFit method
 *			- fixed bugs in addRow, addCol methods
 *			- fixed bugs in initFrame:&etc&etc methods
 *			- improved performance when changing row/column size
 *		Aug 1994:	Steve Quirk
 *			- improved drawing performance
 *			- improved getRow:andCol:forPoint: performance
 *		Sep 1994:  Scott Violet
 *			- improved drawing performance; Scott's note:
 *				In the code for drawSelf:: Mike chose to call drawCellAt::
 *				which is very expensive, this will result in needless
 *				lockFocus: and unlockFocus: calls, and this will make it
 *				so that when the matrix is sent printPSCode: it won't work
 *				(it tries to print it flipped).
 */

#import <misckit/MiscMatrix.h>
#import <objc/List.h>
#import <objc/objc-runtime.h>

#define CLASS_VERSION 		0
#define CLASS_NAME			"MiscMatrix"

// These are the private methods we use in MiscMatrix
@interface MiscMatrix(private)

- _Misc_copyRowSizes:rs andColSizes:cs zone:(NXZone *)zone;
- _Misc_moveColumnsRightBy:(NXCoord)difference startingAt:(int)col;
- _Misc_moveRowsDownBy:(NXCoord)difference startingAt:(int)row;

@end

@implementation MiscMatrix

+ initialize
// Set the class version
{
	if (self == objc_lookUpClass(CLASS_NAME))  {
		[self setVersion:CLASS_VERSION];
	}
	return self;
}

- setupStorage:(int)rowsHigh :(int)colsWide
// Set up our storage objects
{
	int i;
	MiscColumnSize newCSize;
	MiscRowSize newRSize;
	
	columnSizes = [[Storage allocFromZone:[self zone]] initCount:0 
				elementSize:sizeof(MiscColumnSize)
				description:MISC_COLUMNSIZE_DESC];
	[columnSizes setAvailableCapacity:(colsWide > 2) ? colsWide : 2];
	rowSizes = [[Storage allocFromZone:[self zone]] initCount:0 
				elementSize:sizeof(MiscRowSize)
				description:MISC_ROWSIZE_DESC];
	[rowSizes setAvailableCapacity:(rowsHigh > 2) ? rowsHigh : 2];
	
	newCSize.width = cellSize.width;
	newRSize.height = cellSize.height;
	for (i=0; i<colsWide; i++)  {
		newCSize.x = bounds.origin.x + i * (cellSize.width + intercell.width);
		[columnSizes addElement:&newCSize];
	}
	for (i=0; i<rowsHigh; i++)  {
		newRSize.y = bounds.origin.y + i * (cellSize.height + intercell.height);
		[rowSizes addElement:&newRSize];
	}
	
	return self;
}

- initFrame:(const NXRect *)frm mode:(int)aMode prototype:cellId 
			numRows:(int)rowsHigh numCols:(int)colsWide
// Designated initializer override from Matrix.  Sets up our storage stuff.
{
	[super initFrame:frm mode:aMode prototype:cellId numRows:rowsHigh 
				numCols:colsWide];
	
	[self setupStorage:rowsHigh :colsWide];
	
	return self;
}

- initFrame:(const NXRect *)frm mode:(int)aMode cellClass:factoryId 
			numRows:(int)rowsHigh numCols:(int)colsWide
// Designated initializer override from Matrix.  Sets up our storage stuff.
{
	[super initFrame:frm mode:aMode cellClass:factoryId numRows:rowsHigh 
				numCols:colsWide];
	
	[self setupStorage:rowsHigh :colsWide];
	
	return self;
}

- _Misc_copyRowSizes:rs andColSizes:cs zone:(NXZone *)zone
{
	rowSizes = [rs copyFromZone:zone];
	columnSizes = [cs copyFromZone:zone];
	return self;
}

- copyFromZone:(NXZone *)zone
{
	id obj = [super copyFromZone:zone];
	[obj _Misc_copyRowSizes:rowSizes andColSizes:columnSizes zone:zone];
	return obj;
}

- free
// free the storage
{
	[columnSizes free];
	[rowSizes free];
	return [super free];
}

- _Misc_moveColumnsRightBy:(NXCoord)difference startingAt:(int)col
// A private method used by the methods which cause sizing stuff to change
{
	MiscColumnSize *cSize;
	int i;

	if ((col < 0) || (col >= numCols))  {
		return nil;
	}

	cSize = (MiscColumnSize *)[columnSizes elementAt:col];
	for (i=col; i<numCols; i++, cSize++)
		cSize->x += difference;

	return self;
}

- _Misc_moveRowsDownBy:(NXCoord)difference startingAt:(int)row
// A private method used by the methods which cause sizing stuff to change
{
	MiscRowSize *rSize;
	int i;
	
	if ((row < 0) || (row >= numRows))  {
		return nil;
	}

	rSize = (MiscRowSize *)[rowSizes elementAt:row];
	for (i=row; i<numRows; i++, rSize++)
		rSize->y += difference;

	return self;
}

- setWidth:(NXCoord)newWidth ofCol:(int)col
// This method allows the setting of column widths
{
	NXCoord diff;
	MiscColumnSize *cSize;
	
	if ((col < 0) || (col >= numCols))  {
		return nil;
	}

	cSize = (MiscColumnSize *)[columnSizes elementAt:col];
	diff = newWidth - cSize->width;
	cSize->width = newWidth;
	
	return [self _Misc_moveColumnsRightBy:diff startingAt:col+1];
}

- setHeight:(NXCoord)newHeight ofRow:(int)row
// This method allows the setting of row heights
{
	NXCoord diff;
	MiscRowSize *rSize;
	
	if ((row < 0) || (row >= numRows))  {
		return nil;
	}

	rSize = (MiscRowSize *)[rowSizes elementAt:row];
	diff = newHeight - rSize->height;
	rSize->height = newHeight;

	return [self _Misc_moveRowsDownBy:diff startingAt:row+1];
}

- sizeToCells
// Resize the matrix to the proper size to fit all our cells.
{
	NXRect rect;
	MiscColumnSize *cSize;
	MiscRowSize *rSize;
	
	[self getFrame:&rect];
	
	if (numCols == 0)  {
		rect.size.width = 0.0;
	}  else  {
		cSize = (MiscColumnSize *)[columnSizes lastElement];
		rect.size.width = cSize->x + cSize->width - bounds.origin.x;
	}

	if (numRows == 0)  {
		rect.size.height = 0.0;
	}  else  {
		rSize = (MiscRowSize *)[rowSizes lastElement];
		rect.size.height = rSize->y + rSize->height - bounds.origin.y;
	}
	
	/* Or do we want to just hit the instance variable directly--see the
	 * version at the end of -sizeRowsToFitCells where you _cannot_ use
	 * the -setFrame: method.  If you have troubles with this implementation,
	 * let the maintainer of the class know to change this so it will
	 * work right for everybody.
	 */
	[self setFrame:&rect]; // I think we want self here, not super...  -DAY

	return self;
}


- sizeToFit
/*
 *	resize row heights and column widths to accommodate the largest cell in each
 *	then resize self to fit the cells.
 */
{
	int row,col;
	NXSize thisCellSize;
	MiscRowSize	*rSize;
	MiscColumnSize *cSize;
	NXCoord *maxWidth, *mW, maxHeight;
	NXZone *myZone;
	id	*cellAt;
	/* 
	 *	This method is a pig.  Try to improve preformance by removing method calls
	 *	by using the method implementation ptr...
	 */
	id (*cellCalcCellSize)(id, SEL, NXSize*);

	myZone = [super zone];
	
	// space to record the largest size of the columns
	maxWidth = NXZoneCalloc(myZone, numCols, sizeof(NXCoord));

	/*
	 *	this monkey business is to speed up execution of this pig
	 */
	cellCalcCellSize = (id(*)(id, SEL, NXSize*))
		[protoCell methodFor:@selector(calcCellSize:)];
	cellAt = ((List *)(cellList))->dataPtr;				// peek at Lists' data
	
	rSize = (MiscRowSize *)[rowSizes elementAt:0];
	for (row = 0; row < numRows; row++,rSize++) {
		maxHeight = rSize->height;				/* init max = height of cell 0 */
		
		for (col = 0, mW = maxWidth; col < numCols; col++, mW++) {
//			[[super cellAt:row:col] calcCellSize:&thisCellSize];
			//	replace above with....
			cellCalcCellSize(*cellAt,@selector(calcCellSize:),&thisCellSize);
			
			if (thisCellSize.height > maxHeight)
				maxHeight = thisCellSize.height;
			if (thisCellSize.width > *mW)
				*mW = thisCellSize.width;
			cellAt++;	/* next column... */
		}
		/*
		 *	change the height of this row if one of the columns needs more headroom
		 */
		if (maxHeight > rSize->height)
			[self setHeight:maxHeight ofRow:row];
	}

	/*
	 *	now adjust the column widths if needed...
	 */
	cSize = (MiscColumnSize *)[columnSizes elementAt:0];
	for (col = 0, mW = maxWidth; col < numCols; col++,cSize++, mW++)
		if (*mW != cSize->width) {
			NXCoord diff;
			diff = *mW - cSize->width;
			cSize->width = *mW;
			[self _Misc_moveColumnsRightBy:diff startingAt:col+1];
		}

	if (maxWidth)
		NXZoneFree(myZone,maxWidth);

	return [self sizeToCells];
}

- renewRows:(int)newRows cols:(int)newCols
// Makes sure to keep our storage objects in synch with everything else.
{
	MiscColumnSize newCSize, *cSize;
	MiscRowSize newRSize, *rSize;
	int i;
	
	// Remove any storage elements past the new number of cols
	for (i=numCols-1; i>=newCols; i--)  {
		[columnSizes removeLastElement];
	}
	// Add any needed new storage elements to get up to the new number of cols
	for (i=numCols; i<newCols; i++)  {
		if (i==0)  {
			newCSize.x = bounds.origin.x;
		}  else  {
			cSize = (MiscColumnSize *)[columnSizes lastElement];
			newCSize.x = cSize->x + cSize->width + intercell.width;
		}
		newCSize.width = cellSize.width;
		[columnSizes addElement:&newCSize];
	}

	// Remove any storage elements past the new number of rows
	for (i=numRows-1; i>=newRows; i--)  { // Was ++ but -- makes more sense
		[rowSizes removeLastElement];
	}
	// Add any needed new storage elements to get up to the new number of rows
	for (i=numRows; i<newRows; i++)  {
		if (i==0)  {
			newRSize.y = bounds.origin.y;
		}  else  {
			rSize = (MiscRowSize *)[rowSizes lastElement];
			newRSize.y = rSize->y + rSize->height + intercell.height;
		}
		newRSize.height = cellSize.height;
		[rowSizes addElement:&newRSize];
	}
	
	[super renewRows:newRows cols:newCols];
	
	return self;
}

- addCol
/*
 *	addCol is implemented by the Matrix superclass as [self insertColAt:numCols]
 *	so doing all this work here is redundant (and wrong).  Changed this to simply
 *	call super.
 */
{	
	int n = [columnSizes count];		/*	copy just to prove a point... */
	
	[super addCol];
	//	to prove my point, look how everything still checks out...
	if (([columnSizes count] != (n+1)) || ((n+1) != numCols)) 
	  	[self error:"Assertion failed in [MiscMatrix addCol]. Re-implement!"];

	return self;
}

- addRow
/*
 *	addRow is implemented by the Matrix superclass as [self insertRowAt:numRows]
 *	so doing all this work here is redundant (and wrong).  Changed this to simply
 *	call super.
 */
{
	int n = [rowSizes count];		/*	copy just to prove a point... */
		
	[super addRow];
	//	to prove my point, look how everything still checks out...
	if (([rowSizes count] != (n+1)) || ((n+1) != numRows)) 
	  	[self error:"Assertion failed in [MiscMatrix addRow]. Re-implement!"];

	return self;
}

- insertColAt:(int)col
// Keep the storage in synch
{
	MiscColumnSize newCSize, *cSize;
	
	if ((col < 0) || (col > numCols))
		return nil;

	newCSize.width = cellSize.width;
	if (col == 0)						/* adding first cell ? */
		newCSize.x = bounds.origin.x;
	else 
		if (col == numCols) {			/* adding new last element? */
			cSize = (MiscColumnSize *)[columnSizes lastElement];
			newCSize.x = cSize->x + cSize->width + intercell.width;
		} else {						/* must be in the middle somewhere... */
			cSize = (MiscColumnSize *)[columnSizes elementAt:col];
			newCSize.x = cSize->x;
		}


	if (col == numCols)
		[columnSizes addElement:&newCSize];
	else {
		[columnSizes insertElement:&newCSize at:col];
		[self _Misc_moveColumnsRightBy:newCSize.width + intercell.width 
					startingAt:col+1];
	}
	
	[super insertColAt:col];

	return self;
}

- insertRowAt:(int)row
// Keep the storage in synch
{
	MiscRowSize newRSize, *rSize;
	
	if ((row < 0) || (row > numRows))
		return nil;
	
	newRSize.height = cellSize.height;
	if (row == 0)						/* adding first row? */
		newRSize.y = bounds.origin.y;
	else
		if (row == numRows) {			/* adding new last row? */
			rSize = (MiscRowSize *)[rowSizes lastElement];
			newRSize.y = rSize->y + rSize->height + intercell.height;
		} else {						/* must be in the middle somewhere... */
			rSize = (MiscRowSize *)[rowSizes elementAt:row];
			newRSize.y = rSize->y;
		}
		

	if (row == numRows)
		[rowSizes addElement:&newRSize];
	else {
		[rowSizes insertElement:&newRSize at:row];			// changed from row+1 (sq)
		[self _Misc_moveRowsDownBy:newRSize.height + intercell.height 
				startingAt:row+1];
	}
	
	[super insertRowAt:row];
	
	return self;
}

- removeColAt:(int)col andFree:(BOOL)flag
// Keep the storage in synch
{
	MiscColumnSize *cSize;
	NXCoord diff;
	
	if ((col >= numCols) || (col < 0))  {
		return nil;
	}
	
	[super removeColAt:col andFree:flag];

	cSize = (MiscColumnSize *)[columnSizes elementAt:col];
	diff = cSize->width;
	[columnSizes removeElementAt:col];
	[self _Misc_moveColumnsRightBy:0.0 - diff - intercell.width	startingAt:col];
	
	return self;
}

- removeRowAt:(int)row andFree:(BOOL)flag
// Keep the storage in synch
{
	MiscRowSize *rSize;
	NXCoord diff;
	
	if ((row >= numRows) || (row < 0))  {
		return nil;
	}

	[super removeRowAt:row andFree:flag];

	rSize = (MiscRowSize *)[rowSizes elementAt:row];
	diff = rSize->height;
	[rowSizes removeElementAt:row];
	[self _Misc_moveRowsDownBy:0.0 - diff - intercell.height startingAt:row];
	
	return self;
}

- drawSelf:(const NXRect *)rects:(int)rectCount
// We do our own drawing because we need to draw our cells in diverse 
// rectangles
{
	int row, col;
	int rMin,cMin,rMax,cMax;
	NXPoint lowerRt;
	NXRect cFrm;
	MiscColumnSize *cSize;
	MiscRowSize *rSize;

// Scott Violet removed the window flushing stuff; his change in the
// cell rendering should make these unnecessary, so removing them will
// remove flicker when the MiscMatrix is in a SplitView.  I simply
// commented it out for now; --DAY
//	[window disableFlushWindow];
	
	// the background (if any)
	if (backgroundGray != -1.0)  {
		PSsetgray(backgroundGray);
		if (rectCount==1)  {
			NXRectFill(&(rects[0]));
		}  else  {
			NXRectFill(&(rects[1]));
			NXRectFill(&(rects[2]));
		}
	}
	
	/*
	 *	calculate the cells that need to be redrawn & iterate through them only...
	 */
	[self getRow:&rMin andCol:&cMin forPoint:&(rects->origin)];
	if (rMin == -1)		rMin = 0;
	if (cMin == -1)		cMin = 0;
	
	lowerRt.x = rects->origin.x + rects->size.width;
	lowerRt.y = rects->origin.y + rects->size.height;
	[self getRow:&rMax andCol:&cMax forPoint:&lowerRt];
	if (rMax == -1)		rMax = numRows-1;
	if (cMax == -1)		cMax = numCols-1;
	
	rSize = (MiscRowSize *)[rowSizes elementAt:rMin];
	for (row=rMin; row<=rMax; row++,rSize++)  {
		cSize = (MiscColumnSize *)[columnSizes elementAt:cMin];
		for (col=cMin; col<=cMax; col++,cSize++)  {
			cFrm.origin.x = cSize->x;
			cFrm.origin.y = rSize->y;
			cFrm.size.width = cSize->width;
			cFrm.size.height = rSize->height;
			if (rectCount == 1) {
				if (NXIntersectsRect(&(rects[0]), &cFrm))
					// old way: [self drawCellAt:row:col];
					[self getCellFrame:&cFrm at:row:col];
					[[self cellAt:row:col] drawInside:&cFrm inView:self];
			} else {
				if ((NXIntersectsRect(&(rects[1]), &cFrm)) || 
							(NXIntersectsRect(&(rects[2]), &cFrm)))
					// old way: [self drawCellAt:row:col];
					[self getCellFrame:&cFrm at:row:col];
					[[self cellAt:row:col] drawInside:&cFrm inView:self];
			}
		}
	}

//	[window reenableFlushWindow];
//	[window flushWindow];
	
	return self;
}

- getCellFrame:(NXRect *)theRect at:(int)row:(int)col
// Calculate and return the rect used to display the cell at the given
// row and column
{
	MiscColumnSize *cSize;
	MiscRowSize *rSize;
	
	if (col < numCols)  {
		cSize = (MiscColumnSize *)[columnSizes elementAt:col];
		theRect->origin.x = cSize->x;
		theRect->size.width = cSize->width;
	}  else  {
		int num = col - numCols;
		
		cSize = (MiscColumnSize *)[columnSizes lastElement];
		theRect->origin.x = cSize->x +
					(num * (cellSize.width + intercell.width));
		theRect->size.width = cellSize.width;
	}
	
	if (row < numRows)  {
		rSize = (MiscRowSize *)[rowSizes elementAt:row];
		theRect->origin.y = rSize->y;
		theRect->size.height = rSize->height;
	}  else  {
		int num = row - numRows;
		
		rSize = (MiscRowSize *)[rowSizes lastElement];
		theRect->origin.y = rSize->y +
					(num * (cellSize.height + intercell.height));
		theRect->size.height = cellSize.height;
	}
	
	return self;
}

- getRow:(int *)row andCol:(int *)col forPoint:(const NXPoint *)aPoint
// Calculate the row and column of the cell which contains the given point
//	changed to use a bsearch instead of iterative...		- sq
{
	MiscColumnSize *cSize;
	MiscRowSize *rSize;
	int	index, upper, lower;
	
	*row = -1;
	*col = -1;
	if ((aPoint->x < bounds.origin.x) || 
				(aPoint->x > bounds.origin.x + bounds.size.width) || 
				(aPoint->y < bounds.origin.y) || 
				(aPoint->y > bounds.origin.y + bounds.size.height))  {
		return nil;
	}
	cSize = (MiscColumnSize *)[columnSizes elementAt:0];
	lower = 0;
	upper = [columnSizes count];
	while (lower < upper) {
		index = (lower + upper) / 2;
		if (aPoint->x < cSize[index].x)
			upper = index;					// lower, look to the left...
		else if (aPoint->x > (cSize[index].x + cSize[index].width))
			lower = index + 1;				// higher, look to the right...
		else {
			*col = index;					// hit it...
			break;
		}
	}
	rSize = (MiscRowSize *)[rowSizes elementAt:0];
	lower = 0;
	upper = [rowSizes count];
	while (lower < upper) {
		index = (lower + upper) / 2;
		if (aPoint->y < rSize[index].y)
			upper = index;					// lower, look to the left...
		else if (aPoint->y > (rSize[index].y + rSize[index].height))
			lower = index + 1;				// higher, look to the right...
		else {
			*row = index;					// hit it...
			break;
		}
	}
	return ((*row == -1) || (*col == -1)) ? nil : self;
}

- setIntercell:(const NXSize *)aSize
// Keep the storage in synch
{
	NXCoord xDiff = aSize->width - intercell.width;
	NXCoord yDiff = aSize->height - intercell.height;
	MiscRowSize *rSize;
	MiscColumnSize *cSize;
	int i;
	
	for (i=1; i<numRows; i++)  {
		rSize = (MiscRowSize *)[rowSizes elementAt:i];
		rSize->y += (yDiff * i);
	}
	for (i=1; i<numCols; i++)  {
		cSize = (MiscColumnSize *)[columnSizes elementAt:i];
		cSize->x += (xDiff * i);
	}
	
	return [super setIntercell:aSize];
}


- write:(NXTypedStream *)typedStream
// Write our ivars
{
	[super write:typedStream];
	NXWriteObject(typedStream, columnSizes);
	NXWriteObject(typedStream, rowSizes);
	return self;
}

- read:(NXTypedStream *)typedStream
// Read our ivars
{
	int classVersion;

	[super read:typedStream];
	
	classVersion = NXTypedStreamClassVersion(typedStream, CLASS_NAME);
	
	switch (classVersion)  {
		case 0:		// First version.
			columnSizes = NXReadObject(typedStream);
			rowSizes = NXReadObject(typedStream);
			break;
		default:
			NXLogError("[%s read:] class version %d cannot read "
						"instances archived with version %d", 
						CLASS_NAME, CLASS_VERSION, classVersion);
			[self setupStorage:numRows :numCols];
			break;
	}
	return self;
}


// ********************Overridden private methods***********************
// *****************that I'm going to hell for using********************

// These methods are used by Matrix's mouseDown:.  Doing the whole 
// mouseDown: method over would have been a royal pain in the butt, 
// so I cheated.


- (BOOL)_mouseHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
{
	NXPoint	point;
	id		ret;

	point = *forpoint;
	[self convertPoint:&point fromView:nil];
	ret = [self getRow:row andCol:col forPoint:&point];

	if (ret == nil)
		return NO;
	return YES;
}

- (BOOL)_loopHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
{
	NXPoint	point;
	id		ret;

	point = *forpoint;
	[self convertPoint:&point fromView:nil];
	ret = [self getRow:row andCol:col forPoint:&point];

	if (ret == nil)
		return NO;
	return YES;
}

- (BOOL)_radioHit:(const NXPoint *)forpoint row:(int *)row col:(int *)col
{
	NXPoint	point;
	id		ret;

	point = *forpoint;
	[self convertPoint:&point fromView:nil];
	ret = [self getRow:row andCol:col forPoint:&point];

	if (ret == nil)
		return NO;
	return YES;
}

- sizeRowsToFitCells
{
    NXRect	veryBig;
    NXSize	size;
    NXRect	myFrame;
    NXCoord	maxHeight;
    NXCoord	offset = 0.0;
    MiscRowSize	*rSize;
    int		i,j;
    
    veryBig.size.width = cellSize.width;
    veryBig.size.height = MAXFLOAT;
    
    [self getFrame:&myFrame];
    for (i = 0; i < numRows; i++)
    {
	maxHeight = 0.0;
	rSize = (MiscRowSize *)[rowSizes elementAt:i];
	rSize->y = offset;
	for (j = 0; j < numCols; j++)
	{
	    [[self cellAt:i :j] calcCellSize:&size inRect:&veryBig];
	    if (size.height > maxHeight)
		maxHeight = size.height;
	}
	rSize->height = maxHeight;
	offset += maxHeight + intercell.height;
    }
    
    myFrame.size.height = offset;
    // This can't be used:  [self setFrame:&myFrame];
    /*
     * We can't call setFrame: here because it will call sizeTo::
     * which will try to modify cellFrame and the column/row sizes.
     * So we just copy into the instance variable.
	 *
	 * And we'll probably be sent to the deepest corners of hell for doing it.
     */   
    // frame = myFrame;
	// This has been suggested as a perhaps safer way to do the same thing,
	// so we'll use it instead of the above line, but I'm keeping the above
	// until we are sure that it is doing the right thing...  -DAY
	/*
	 * We use super here to avoid re-adjusting the row
	 * and column sizes.
	 */
	[super sizeTo:myFrame.size.width :myFrame.size.height];  
      
    return self;
}
    

- sizeTo:(NXCoord)width :(NXCoord)height
{
    NXRect	oldFrame;
    NXCoord	dx, dy;
    float	sx, sy;
    
    [self getFrame:&oldFrame];
    
    [super sizeTo:width :height];

    dx = width - oldFrame.size.width;
    dy = height - oldFrame.size.height;

    if ([self doesAutosizeCells] && (numRows > 0) && (numCols > 0))
    {
	NXCoord		offset;
	int		i;
	MiscRowSize	*rSize;
	MiscColumnSize	*cSize;
	
	sx = 1 + (dx/(oldFrame.size.width - (numRows - 1)*intercell.width));
	sy = 1 + (dy/(oldFrame.size.height - (numCols - 1)*intercell.height));
	
	for (i=0, offset = 0.0; i < numRows; i++)
	{
	    rSize = (MiscRowSize *)[rowSizes elementAt:i];
	    rSize->y = offset;
	    rSize->height *= floor(sy+0.5);
	    offset += rSize->height + intercell.height;
	}
	
	for (i=0, offset = 0.0; i < numCols; i++)
	{
	    cSize = (MiscColumnSize *)[columnSizes elementAt:i];
	    cSize->x = offset;
	    cSize->width *= sx;
	    offset += cSize->width + intercell.width;
	}
    }
    return self;
}

@end

@implementation Storage(MiscLastElementCategory)
	
- (void *)lastElement
// A little shortcut
{
	void *theLastOne;
	theLastOne = (numElements) ? (char *)dataPtr + (elementSize*(numElements-1)) : NULL;
	return theLastOne;
}
	
@end

